#include <QMutexLocker>
#include "Tape.h"
#include "Logger.h"

#include "CodeTable.h"
extern CodeTable Encoder;


Tape::Tape(CharType::EType type) : _myType(type)
{}

void Tape::PutChar(const QByteArray& chars)
{
    QMutexLocker locker(&_mutex);
    _myType == CharType::DATA ? _tape.append(chars) : _tape.prepend(chars);
    emit NewData();
}

void Tape::PutChar(Char code)
{
    PutChar(QByteArray(1, code));
}

void Tape::SetCharsLookUp(const CharTypeList& _charlist)
{
    _charList.clear();

    Q_FOREACH(CharTypePtr character, _charlist)
        if (character->Type() == _myType)
            _charList.append(character);
}

void Tape::Reset()
{
    _tape.clear();
    _charList.clear();
    _currentChar.reset();
}

void Tape::SetCharAgent(ISimpleChannelPtr agent)
{
    disconnect(_charAgent.get(), 0, this, 0);
    _charAgent = agent;
    connect(_charAgent.get(), SIGNAL(dataNotification(const QByteArray&)), SLOT(PutChar(const QByteArray&)), Qt::DirectConnection);
}

unsigned Tape::GetBytesCountToRead()
{
    unsigned end = 0;
    
    Q_FOREACH(CharTypePtr character, _charList)
    {
        unsigned pos = GetTapeReadPosition(character);
        if (pos > end)
        {
            end = pos;
            _currentChar = character;
        }
    }

    return end;
}

bool Tape::HasChars()
{
    return !_charList.empty() && !_tape.isEmpty();
}

CharTypePtr Tape::Next()
{
    QMutexLocker locker(&_mutex);
    
    unsigned bytesCount = GetBytesCountToRead();

    if (bytesCount)
    {
        _currentChar->SetValue(_tape.left(bytesCount));
        _tape.remove(0, bytesCount);
    }

    return bytesCount ? _currentChar : CharTypePtr(static_cast<CharType*>(0));  
}

unsigned Tape::GetTapeReadPosition(CharTypePtr character)
{    
    return character->IsRegExp() ? MatchRegExp(character) : MatchCharList(character);
}

unsigned Tape::MatchRegExp(CharTypePtr character)
{
    return character->RegExp().indexIn(QString(_tape)) == 0 ? character->RegExp().matchedLength() : 0;  
}

unsigned Tape::MatchCharList(CharTypePtr character)
{
    unsigned readPosition = 0;

    if (character->Match() != CharType::NONE)
    {
        unsigned redline = _tape.length();

        Q_FOREACH(Char code, character->Impossible())
            redline = qMin<unsigned>(redline, _tape.indexOf(code));   

        if (character->Match() == CharType::ANY)
            readPosition = redline;
        else
            while(character->Possible().contains(_tape[readPosition]) && ++readPosition);
    }

    return readPosition;
}

CharType::EType Tape::Type()
{
    return _myType;
}

void Tape::PlaceTimeoutChar()
{
    PutChar(Encoder.Encode("TIMEOUT"));
}